import datetime

import requests, requests.utils, pickle
import requests.packages.urllib3
import os
import time
import json
import random
import re
import argparse
import sys
import threading

requests.packages.urllib3.disable_warnings()
# get function name
FuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name


# class multiThread(threading.Thread):
#     def __init__(self, jd, startTime):
#         threading.Thread.__init__(self)
#         self.jd = jd
#         self.startTime = startTime
#
#     def run(self):
#         while datetime.datetime.now() <= datetime.datetime.strptime(self.startTime, '%Y-%m-%d %H:%M:%S.%f'):
#             time.sleep(0.1)
#         print("put in cart" + str(time.time()))
#         self.jd.getPreSale(options.good)
#         self.jd.order_info(options.submit)
#
#         print(str(time.time()))


class JDWrapper(object):
    '''
    This class used to simulate login JD
    '''

    def __init__(self, usr_name=None, usr_pwd=None):
        # cookie info
        self.trackid = ''
        self.uuid = ''
        self.eid = ''
        self.fp = ''

        self.usr_name = usr_name
        self.usr_pwd = usr_pwd

        self.interval = 0

        # init url related
        self.home = 'https://passport.jd.com/new/login.aspx'
        self.login = 'https://passport.jd.com/uc/loginService'
        self.imag = 'https://authcode.jd.com/verify/image'
        self.auth = 'https://passport.jd.com/uc/showAuthCode'

        self.sess = requests.Session()

        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36',
            'ContentType': 'text/html; charset=utf-8',
            'Accept-Encoding': 'gzip, deflate, sdch',
            'Accept-Language': 'zh-CN,zh;q=0.8',
            'Connection': 'keep-alive',
        }

        self.cookies = {

        }

    def login_by_QR(self):
        # jd login by QR code
        try:
            print('+++++++++++++++++++++++++++++++++++++++++++++++++++++++')
            print(u'{0} > 请打开京东手机客户端，准备扫码登陆:'.format(time.ctime()))

            urls = (
                'https://passport.jd.com/new/login.aspx',
                'https://qr.m.jd.com/show',
                'https://qr.m.jd.com/check',
                'https://passport.jd.com/uc/qrCodeTicketValidation'
            )

            # step 1: open login page
            resp = self.sess.get(
                urls[0],
                headers=self.headers
            )
            if resp.status_code != requests.codes.OK:
                print(u'获取登录页失败: %u' % resp.status_code)
                return False

            # save cookies
            for k, v in resp.cookies.items():
                self.cookies[k] = v

            # step 2: get QR image
            resp = self.sess.get(
                urls[1],
                headers=self.headers,
                cookies=self.cookies,
                params={
                    'appid': 133,
                    'size': 147,
                    't': int(time.time() * 1000)
                }
            )
            if resp.status_code != requests.codes.OK:
                print(u'获取二维码失败: %u' % resp.status_code)
                return False

            # save cookies
            for k, v in resp.cookies.items():
                self.cookies[k] = v

            # save QR code
            image_file = 'qr.png'
            with open(image_file, 'wb') as f:
                for chunk in resp.iter_content(chunk_size=1024):
                    f.write(chunk)

            # scan QR code with phone
            if os.name == "nt":
                # for windows
                os.system('start ' + image_file)
            else:
                if os.uname()[0] == "Linux":
                    # for linux platform
                    os.system("eog " + image_file)
                else:
                    # for Mac platform
                    os.system("open " + image_file)

            # step 3: check scan result
            # mush have
            self.headers['Host'] = 'qr.m.jd.com'
            self.headers['Referer'] = 'https://passport.jd.com/new/login.aspx'

            # check if QR code scanned
            qr_ticket = None
            retry_times = 100
            while retry_times:
                retry_times -= 1
                resp = self.sess.get(
                    urls[2],
                    headers=self.headers,
                    cookies=self.cookies,
                    params={
                        'callback': 'jQuery%u' % random.randint(100000, 999999),
                        'appid': 133,
                        'token': self.cookies['wlfstk_smdl'],
                        '_': int(time.time() * 1000)
                    }
                )

                if resp.status_code != requests.codes.OK:
                    continue

                n1 = resp.text.find('(')
                n2 = resp.text.find(')')
                rs = json.loads(resp.text[n1 + 1:n2])

                if rs['code'] == 200:
                    print(u'{} : {}'.format(rs['code'], rs['ticket']))
                    qr_ticket = rs['ticket']
                    break
                else:
                    print(u'{} : {}'.format(rs['code'], rs['msg']))
                    time.sleep(3)

            if not qr_ticket:
                print(u'二维码登陆失败')
                return False

            # step 4: validate scan result
            # must have
            self.headers['Host'] = 'passport.jd.com'
            self.headers['Referer'] = 'https://passport.jd.com/uc/login?ltype=logout'
            resp = self.sess.get(
                urls[3],
                headers=self.headers,
                cookies=self.cookies,
                params={'t': qr_ticket},
            )
            if resp.status_code != requests.codes.OK:
                print(u'二维码登陆校验失败: %u' % resp.status_code)
                return False

            # 京东有时候会认为当前登录有危险，需要手动验证
            # url: https://safe.jd.com/dangerousVerify/index.action?username=...
            res = json.loads(resp.text)
            if not resp.headers.get('P3P'):
                if res.has_key('url'):
                    print(u'需要手动安全验证: {0}'.format(res['url']))
                    return False
                else:
                    # print_json(res)
                    print(u'登陆失败!!')
                    return False

            # login succeed
            self.headers['P3P'] = resp.headers.get('P3P')
            for k, v in resp.cookies.items():
                self.cookies[k] = v

            with open('cookie', 'wb') as f:
                pickle.dump(self.cookies, f)

            print(u'登陆成功')
            return True

        except (Exception) as e:
            print('Exp:', e)
            raise

    def getPreSale(self, stock_id, payload_info):

        try:
            resp = self.sess.get('https://yushou.jd.com/youshouinfo.action', parms=payload_info, cookies=self.cookies)
            # print("json finished re start:"+str(time.time()))
            # content = str(resp.text)
            while re.search('info":"(.*?)"', resp.text).group(0) == 'info":"\\u9884\\u7ea6\\u8fdb\\u884c\\u4e2d"':
                resp = self.sess.get('https://yushou.jd.com/youshouinfo.action', params=payload_info,
                                     cookies=self.cookies)
            link = re.search('url":"(.*?)"', resp.text).group(0)[6:-1]
            # print("re end put in cart start:" + str(time.time()))
            self.sess.get('https:' + link)
            # print("put in cart finished" + str(time.time()))
        except Exception as e:
            print('Exp {0} : {1}'.format(FuncName(), e))

    def good_stock(self, stock_id):

        # 33 : on sale,
        # 34 : out of stock

        # http://ss.jd.com/ss/areaStockState/mget?app=cart_pc&ch=1&skuNum=3180350,1&area=1,72,2799,0
        #   response: {"3180350":{"a":"34","b":"1","c":"-1"}}
        # stock_url = 'http://ss.jd.com/ss/areaStockState/mget'

        # http://c0.3.cn/stocks?callback=jQuery2289454&type=getstocks&skuIds=3133811&area=1_72_2799_0&_=1490694504044
        #   jQuery2289454({"3133811":{"StockState":33,"freshEdi":null,"skuState":1,"PopType":0,"sidDely":"40","channel":1,"StockStateName":"现货","rid":null,"rfg":0,"ArrivalDate":"","IsPurchase":true,"rn":-1}})
        # jsonp or json both work
        stock_url = 'https://c0.3.cn/stocks'

        payload = {
            'type': 'getstocks',
            'skuIds': str(stock_id),
            'area': '17_1381_50718_0',  # area change as needed
        }

        try:
            # get stock state
            resp = self.sess.get(stock_url, params=payload)
            if resp.status_code != 200:
                print(u'获取商品库存失败')
                return 0, ''

            # return json
            stat = re.search('StockState":(.*?),', str(resp.text)).group(0)[12:-1]
            if stat != '33':
                return False

            # 33 : on sale, 34 : out of stock, 36: presell
            return True

        except Exception as e:
            print('Stocks Exception:', e)
            time.sleep(5)

    def order_info(self,  payload_getOrderInfo, submit=False,):

        try:
            # get preorder page
            # print('form order:' + str(time.time()))
            self.sess.get('http://trade.jd.com/shopping/order/getOrderInfo.action', params=payload_getOrderInfo, cookies=self.cookies)

            # just test, not real order
            if not submit:
                return True

            # order info
            payload_submitOrder = {
                'overseaPurchaseCookies': '',
                'submitOrderParam.btSupport': '1',
                'submitOrderParam.ignorePriceChange': '0',
                'submitOrderParam.sopNotPutInvoice': 'false',
                'submitOrderParam.trackID': self.trackid,
                'submitOrderParam.eid': self.eid,
                'submitOrderParam.fp': self.fp,
            }

            rp = self.sess.post('http://trade.jd.com/shopping/order/submitOrder.action', params=payload_submitOrder, cookies=self.cookies)

            if rp.status_code == 200:
                js = json.loads(rp.text)
                if js['success'] is True:
                    print(u'下单成功！订单号：{0}'.format(js['orderId']))
                    print(u'请前往东京官方商城付款')
                    print(time.time())
                    return True
                else:
                    print(u'下单失败！<{0}: {1}>'.format(js['resultCode'], js['message']))
                    if js['resultCode'] == '60017':
                        # 60017: 您多次提交过快，请稍后再试
                        time.sleep(1)
                    return False
            else:
                print(u'请求失败. StatusCode:', rp.status_code)
                return False

        except Exception as e:
            print('Exp {0} : {1}'.format(FuncName(), e))

        return False


def main(options):
    jd = JDWrapper()

    if not jd.login_by_QR():
        return
    # thread1 = multiThread(jd, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'))
    # thread2 = multiThread(jd, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'))
    # thread1.start()
    # thread2.start()
    # thread1.join()
    # thread2.join()

    payload_info = {
        'callback': 'fetchJSON',
        'sku': str(options.good),
    }

    payload_getOrderInfo = {
        'rid': str(int(time.time() * 1000)),
    }

    tryCount = 1

    # while not jd.good_stock(options.good) and datetime.datetime.now() <= datetime.datetime.strptime('2018-04-12 09:55:00.0', '%Y-%m-%d %H:%M:%S.%f'):
    #     time.sleep(10)

    while datetime.datetime.now() <= datetime.datetime.strptime('2018-04-12 10:00:00.0', '%Y-%m-%d %H:%M:%S.%f'):
        # print(time.time())
        time.sleep(0.001)

    while True:
        jd.getPreSale(options.good, payload_info)
        if jd.order_info(payload_getOrderInfo, options.submit) is True or tryCount > 5:
            break
        tryCount += 1
        time.sleep(2)

    if tryCount <= 5:
        return

    while True:
        if jd.good_stock(options.good):
            jd.getPreSale(options.good, payload_info)
            if jd.order_info(payload_getOrderInfo, options.submit) is True:
                break
        time.sleep(10)


if __name__ == '__main__':
    # help message
    parser = argparse.ArgumentParser(description='Simulate to login Jing Dong, and buy sepecified good')

    parser.add_argument('-a', '--area',
                        help='', default='17_1381_50718_0')  # 设置为武汉市洪山区 17_1381_50718_0
    parser.add_argument('-g', '--good',  # 商品编号
                        help='Jing Dong good ID', default='')
    parser.add_argument('-c', '--count', type=int,
                        help='The count to buy', default=1)
    parser.add_argument('-w', '--wait',
                        type=int, default=500,
                        help='Flush time interval, unit MS')  # 刷新间隔
    parser.add_argument('-f', '--flush',
                        action='store_true',
                        help='Continue flash if good out of stock')  # 是否循环刷新抢购
    parser.add_argument('-s', '--submit',
                        action='store_true',
                        help='Submit the order to Jing Dong')  # 是否真正提交

    # example goods
    mix2s_black_8_256 = '6735798'
    oppo_r15 = '6773559'
    options = parser.parse_args()
    # print(options)

    # if options.good == '':
    #     options.good = mix2s_black_8_256
    #     options.submit = True
    # for test
    options.good = mix2s_black_8_256
    options.submit = True

    main(options)
